/*
 * COPYRIGHT:	2010 Christopher Smith, licenced under the GNU GPL v2; see COPYING in the top level directory
 * PROJECT:		sdedit
 * FILE:		winbase.cpp
 * PURPOSE:		base class for C++ class-wrapped windows
 *				
 */

ULONG WindowInitTls = 0;

void InitWinBaseHelp()
{
	WindowInitTls = TlsAlloc();
}

void FreeWinBaseHelp()
{
	TlsFree(WindowInitTls);
}

class WindowBase : public auto_refctd_std_base
{
public:
	HWND Wnd;

protected:
	WindowBase() :
		Wnd(NULL)
	{}
	
	virtual void Init() {}

	virtual LRESULT RecieveMessage(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam) = 0;

	virtual BOOLEAN UseDialogProcessing()
	{
		return TRUE;
	}

	virtual HACCEL GetAcceleratorHandle()
	{
		return NULL;
	}

	void Create(HWND Wnd)
	{
		inc_ref(); //the reference from the underlying window
		this->Wnd = Wnd;

		SetWindowLongPtr(Wnd, GWLP_USERDATA, (ULONG_PTR)this);
		Init();
	}

	LRESULT QueryPreProcess(WPARAM wparam, LPARAM lparam)
	{
		MSG_PRE_PROCESS_OPTIONS *Ppo = reinterpret_cast<MSG_PRE_PROCESS_OPTIONS *>(wparam);
		Ppo->ProcessDialog = UseDialogProcessing();
		Ppo->AcceleratorTable = GetAcceleratorHandle();

		return 0;
	}

	void PrepareForCreate()
	{
		TlsSetValue(WindowInitTls, this);
	}

	static BOOLEAN GetWindowObject(WindowBase *&wo1, HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
	{
		union
		{
			WindowBase **wo;
			LONG_PTR *wol;
			ULONG_PTR pwol;
		};

		wo = &wo1;

		*wol = GetWindowLongPtr(wnd, GWLP_USERDATA);

		if(*wol == -1L) //window destroyed, object deleted
			return FALSE;

		if(!*wo) //must be during create, GWLP_USERDATA not initialized yet
		{
			*wo = reinterpret_cast<WindowBase *>(TlsGetValue(WindowInitTls)); //tls value set in PrepareForCreate

			(*wo)->Wnd = wnd;

			SetWindowLongPtr(wnd, GWLP_USERDATA, *wol);
			
			(*wo)->Init();
		}

		if(msg == WM_NCDESTROY)
		{
			(*wo)->Wnd = NULL; //must be first wo destroy step
			SetWindowLongPtr(wnd, GWLP_USERDATA, -1L);
			(*wo)->dec_ref();
			return FALSE;
		}

		return TRUE;
	}

public:

	virtual ~WindowBase()
	{
		if(Wnd) //if not, window already destroyed
		{
			LONG_PTR wol = GetWindowLongPtr(Wnd, GWLP_USERDATA);
			if(wol == -1L) //window already destroyed
				return;
			SetWindowLongPtr(Wnd, GWLP_USERDATA, -1L);
			DestroyWindow(Wnd);
		}
	}
};